package cn.lishiyuan.leetcode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 51. N 皇后
 * 同面试题 08.12. 八皇后
 *
 * 按照国际象棋的规则，皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
 *
 * n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上，并且使皇后彼此之间不能相互攻击。
 *
 * 给你一个整数 n ，返回所有不同的 n 皇后问题 的解决方案。
 *
 * 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案，该方案中 'Q' 和 '.' 分别代表了皇后和空位。
 *
 *
 *
 * 示例 1：
 *
 * 输入：n = 4
 * 输出：[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
 * 解释：如上图所示，4 皇后问题存在两个不同的解法。
 * 示例 2：
 *
 * 输入：n = 1
 * 输出：[["Q"]]
 */
public class LeetCode51 {

    private static final String QUEEN = "Q";

    private static final String SPACE = "*";

    /**
     * 回溯法处理
     * @param n
     * @return
     */
    public static List<List<String>> solveNQueens(int n) {
        int[] state = new int[n];
        Arrays.fill(state, -1);
        // 考察每一行每一列
        List<List<String>> res = new ArrayList<>();
        nQueen(state,0,n,res);
        return res;
    }

    /**
     * 回溯法处理
     * @param n
     * @return
     */
    private static void nQueen(int[] state, int rows, int n,List<List<String>> res){
        if(rows == n){
            // 前面的处理都没有问题，构建结果
            List<String> list = new ArrayList<>();
            for (int i = 0; i < state.length; i++) {
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < state.length; j++) {
                    if(state[i] == j){
                        sb.append("Q");
                    }else {
                        sb.append("*");
                    }
                }
                list.add(sb.toString());
            }
            res.add(list);
            return;
        }

        for (int j = 0; j < n; j++) {
            if(!cross(state, rows, j)){
                state[rows] = j;
                // 下一行
                nQueen(state, rows + 1, n,res);
            }
        }
    }

    private static boolean cross(int[] state,int row,int col) {
        // 同一行肯定没有交叉
        // 需要检查同一列和斜边情况
        for (int i = row-1; i >=0 ; i--) {
            // 检查三个位置
            int before = state[i];
            if (before == col) {
                // 在同一列
                return true;
            }
            // 斜边
            int distance = row - i;
            // 右倾斜边,左倾斜边,前面的行一定已经处理了
            if (before == (distance + col) || (before == (col - distance))) {
                return true;
            }
        }
        return false;
    }

}
